From f6658ebfc2a3c12a42af0b392492d2d1ce83218d Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Mon, 21 Jul 2003 14:09:51 +0000 Subject: [PATCH] bitkeeper revision 1.370 (3f1bf42fuDsd9ULrhDoxWR8V_vV2zQ) time.c, hypervisor-if.h, domain.c: Fixed more time code in Xen and Xenolinux. Reduced the chance of guest OS time going backwards. Better fixed-point arithmetic when calculating current system time. --- xen/arch/i386/time.c | 95 +++++++++---------- xen/common/domain.c | 2 - xen/include/hypervisor-ifs/hypervisor-if.h | 21 ++-- .../arch/xeno/kernel/time.c | 93 +++++++----------- 4 files changed, 90 insertions(+), 121 deletions(-) diff --git a/xen/arch/i386/time.c b/xen/arch/i386/time.c index 15b5d333f9..b0159250c2 100644 --- a/xen/arch/i386/time.c +++ b/xen/arch/i386/time.c @@ -19,19 +19,6 @@ **************************************************************************** */ -/* - * Note from KAF: We should probably be more careful about overflow - * of our timestamp cycle counter value, as we only use 31 bits of - * precision. This will overflow on a 3GHz processor in less than a second, - * and the situation is only going to get worse. - * - * Probably we should use bits N-N+31 of the TSC rather than 0-31, and - * adjust scale_f and scale_i accordingly. If we're really smart we'd - * calculate N dynamically, according to the measured CPU speed! - * - * I think the current code limps along okay for now though. - */ - /* * linux/arch/i386/kernel/time.c * @@ -65,6 +52,9 @@ unsigned long cpu_khz; /* Detected as we calibrate the TSC */ unsigned long ticks_per_usec; /* TSC ticks per microsecond. */ +/* We use this to prevent overflow of 31-bit RDTSC "diffs". */ +static unsigned int rdtsc_bitshift; + spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; int timer_ack=0; @@ -288,26 +278,18 @@ s_time_t stime_now; /* time in ns at last timer IRQ */ static inline s_time_t __get_s_time(void) { s32 delta_tsc; - u32 low, pcc; - u64 delta; - s_time_t now; - - pcc = stime_pcc; - now = stime_now; - - /* - * We only use the bottom 32 bits of the TSC. This should be sufficient, - * although we take care that TSC on this CPU may be lagging the master TSC - * slightly. In this case we clamp the TSC difference to a minimum of zero. - */ - rdtscl(low); - delta_tsc = low - pcc; + u32 low; + u64 delta, tsc; + + rdtscll(tsc); + low = (u32)(tsc >> rdtsc_bitshift); + delta_tsc = (s32)(low - stime_pcc); if ( unlikely(delta_tsc < 0) ) delta_tsc = 0; delta = ((u64)delta_tsc * st_scale_f); delta >>= 32; delta += ((u64)delta_tsc * st_scale_i); - return now + delta; + return stime_now + delta; } s_time_t get_s_time(void) @@ -359,11 +341,13 @@ void update_dom_time(shared_info_t *si) unsigned long flags; spin_lock_irqsave(&stime_lock, flags); - si->system_time = stime_now; - si->st_timestamp = stime_pcc; - si->tv_sec = wall_clock_time.tv_sec; - si->tv_usec = wall_clock_time.tv_usec; - si->wc_timestamp = wctime_st; + si->cpu_freq = cpu_freq; + si->rdtsc_bitshift = rdtsc_bitshift; + si->system_time = stime_now; + si->st_timestamp = stime_pcc; + si->tv_sec = wall_clock_time.tv_sec; + si->tv_usec = wall_clock_time.tv_usec; + si->wc_timestamp = wctime_st; si->wc_version++; spin_unlock_irqrestore(&stime_lock, flags); @@ -420,7 +404,7 @@ static void update_scale(void) cpu_freq = cpu_freqs[freq_index]; /* adjust scaling factor */ - scale = 1000000000LL << 32; + scale = 1000000000LL << (32 + rdtsc_bitshift); scale /= cpu_freq; st_scale_f = scale & 0xffffffff; st_scale_i = scale >> 32; @@ -438,21 +422,15 @@ static void update_time(unsigned long foo) unsigned long flags; s_time_t new_st; unsigned long usec; - static int calls_since_scale_update = 0; + u64 full_pcc; + static int calls_since_scale_update = 0; spin_lock_irqsave(&stime_lock, flags); /* Update system time. */ stime_now = new_st = __get_s_time(); - rdtscl(stime_pcc); - - /* Maybe update our rate to be in sync with the RTC. */ - if ( ++calls_since_scale_update >= - (SCALE_UPDATE_PERIOD/TIME_UPDATE_PERIOD) ) - { - update_scale(); - calls_since_scale_update = 0; - } + rdtscll(full_pcc); + stime_pcc = (u32)(full_pcc >> rdtsc_bitshift); /* Update wall clock time. */ usec = ((unsigned long)(new_st - wctime_st))/1000; @@ -464,6 +442,14 @@ static void update_time(unsigned long foo) wall_clock_time.tv_usec = usec; wctime_st = new_st; + /* Maybe update our rate to be in sync with the RTC. */ + if ( ++calls_since_scale_update >= + (SCALE_UPDATE_PERIOD/TIME_UPDATE_PERIOD) ) + { + update_scale(); + calls_since_scale_update = 0; + } + spin_unlock_irqrestore(&stime_lock, flags); TRC(printk("TIME[%02d] update time: stime_now=%lld now=%lld,wct=%ld:%ld\n", @@ -486,25 +472,31 @@ int __init init_xeno_time() u32 cpu_cycle; /* time of one cpu cyle in pico-seconds */ u64 scale; /* scale factor */ s64 freq_off; + u64 full_pcc; + unsigned int cpu_ghz; spin_lock_init(&stime_lock); - printk("Init Time[%02d]:\n", cpu); + cpu_ghz = (unsigned int)(cpu_freq / 1000000000ULL); + for ( rdtsc_bitshift = 0; cpu_ghz != 0; rdtsc_bitshift++, cpu_ghz >>= 1 ) + continue; + + printk("Init Time[%02d]: %u\n", cpu, rdtsc_bitshift); /* System Time */ cpu_cycle = (u32) (1000000000LL/cpu_khz); /* in pico seconds */ - scale = 1000000000LL << 32; - scale /= cpu_freq; - st_scale_f = scale & 0xffffffff; - st_scale_i = scale >> 32; - /* calculate adjusted frequencies */ freq_off = cpu_freq/1000; /* .1% */ cpu_freqs[0] = cpu_freq + freq_off; cpu_freqs[1] = cpu_freq; cpu_freqs[2] = cpu_freq - freq_off; + scale = 1000000000LL << (32 + rdtsc_bitshift); + scale /= cpu_freq; + st_scale_f = scale & 0xffffffff; + st_scale_i = scale >> 32; + /* Wall Clock time */ wall_clock_time.tv_sec = get_cmos_time(); wall_clock_time.tv_usec = 0; @@ -514,7 +506,8 @@ int __init init_xeno_time() /* set starting times */ stime_now = (s_time_t)0; - rdtscl(stime_pcc); + rdtscll(full_pcc); + stime_pcc = (u32)(full_pcc >> rdtsc_bitshift); wctime_st = NOW(); /* start timer to update time periodically */ diff --git a/xen/common/domain.c b/xen/common/domain.c index 1a36ca9750..de004b0d9d 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -382,7 +382,6 @@ int final_setup_guestos(struct task_struct * p, dom_meminfo_t * meminfo) /* set up the shared info structure */ update_dom_time(p->shared_info); - p->shared_info->cpu_freq = cpu_freq; p->shared_info->domain_time = 0; /* we pass start info struct to guest os as function parameter on stack */ @@ -635,7 +634,6 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params, /* Set up shared info area. */ update_dom_time(p->shared_info); - p->shared_info->cpu_freq = cpu_freq; p->shared_info->domain_time = 0; virt_startinfo_address = (start_info_t *) diff --git a/xen/include/hypervisor-ifs/hypervisor-if.h b/xen/include/hypervisor-ifs/hypervisor-if.h index 0dfd07cb60..fd91a7d579 100644 --- a/xen/include/hypervisor-ifs/hypervisor-if.h +++ b/xen/include/hypervisor-ifs/hypervisor-if.h @@ -193,20 +193,21 @@ typedef struct shared_info_st { /* * Time: The following abstractions are exposed: System Time, Clock Time, * Domain Virtual Time. Domains can access Cycle counter time directly. - * XXX RN: Need something to pass NTP scaling to GuestOS. + * + * The following values are updated periodically (and atomically, from the + * p.o.v. of the guest OS). Th eguest OS detects this because the wc_version + * is incremented. */ - - u64 cpu_freq; /* to calculate ticks -> real time */ - + u32 wc_version; /* a version number for info below */ + unsigned int rdtsc_bitshift; /* use bits N:N+31 of TSC */ + u64 cpu_freq; /* to calculate ticks -> real time */ /* System Time */ - long long system_time; /* in ns */ - unsigned long st_timestamp; /* cyclecounter at last update */ - + long long system_time; /* in ns */ + unsigned long st_timestamp; /* cyclecounter at last update */ /* Wall Clock Time */ - u32 wc_version; /* a version number for info below */ - long tv_sec; /* essentially a struct timeval */ + long tv_sec; /* essentially a struct timeval */ long tv_usec; - long long wc_timestamp; /* system time at last update */ + long long wc_timestamp; /* system time at last update */ /* Domain Virtual Time */ unsigned long long domain_time; diff --git a/xenolinux-2.4.21-sparse/arch/xeno/kernel/time.c b/xenolinux-2.4.21-sparse/arch/xeno/kernel/time.c index 55b100e257..e02f726757 100644 --- a/xenolinux-2.4.21-sparse/arch/xeno/kernel/time.c +++ b/xenolinux-2.4.21-sparse/arch/xeno/kernel/time.c @@ -76,9 +76,9 @@ spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; extern rwlock_t xtime_lock; unsigned long cpu_khz; /* get this from Xen, used elsewhere */ -static spinlock_t hyp_stime_lock = SPIN_LOCK_UNLOCKED; -static spinlock_t hyp_wctime_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t hyp_time_lock = SPIN_LOCK_UNLOCKED; +static unsigned int rdtsc_bitshift; static u32 st_scale_f; static u32 st_scale_i; static u32 shadow_st_pcc; @@ -92,37 +92,22 @@ static s64 shadow_st; * and use the cycle counter value as the "version" number. Clashes * should be very rare. */ -static inline long long get_s_time(void) +static inline s64 __get_s_time(void) { - unsigned long flags; - u32 delta_tsc, low, pcc; - u64 delta; - s64 now; - - spin_lock_irqsave(&hyp_stime_lock, flags); - - while ((pcc = HYPERVISOR_shared_info->st_timestamp) != shadow_st_pcc) - { - barrier(); - shadow_st_pcc = pcc; - shadow_st = HYPERVISOR_shared_info->system_time; - barrier(); - } - - now = shadow_st; - /* only use bottom 32bits of TSC. This should be sufficient */ - rdtscl(low); - delta_tsc = low - pcc; + s32 delta_tsc; + u32 low; + u64 delta, tsc; + + rdtscll(tsc); + low = (u32)(tsc >> rdtsc_bitshift); + delta_tsc = (s32)(low - shadow_st_pcc); + if ( unlikely(delta_tsc < 0) ) delta_tsc = 0; delta = ((u64)delta_tsc * st_scale_f); delta >>= 32; delta += ((u64)delta_tsc * st_scale_i); - spin_unlock_irqrestore(&hyp_time_lock, flags); - - return now + delta; - + return shadow_st + delta; } -#define NOW() ((long long)get_s_time()) /* * Wallclock time. @@ -139,21 +124,35 @@ void do_gettimeofday(struct timeval *tv) unsigned long flags; long usec, sec; u32 version; - u64 now; + u64 now, cpu_freq, scale; - spin_lock_irqsave(&hyp_wctime_lock, flags); + spin_lock_irqsave(&hyp_time_lock, flags); - while ((version = HYPERVISOR_shared_info->wc_version)!= shadow_wc_version) + while ( (version = HYPERVISOR_shared_info->wc_version) != + shadow_wc_version ) { barrier(); + shadow_wc_version = version; shadow_tv_sec = HYPERVISOR_shared_info->tv_sec; shadow_tv_usec = HYPERVISOR_shared_info->tv_usec; shadow_wc_timestamp = HYPERVISOR_shared_info->wc_timestamp; + shadow_st_pcc = HYPERVISOR_shared_info->st_timestamp; + shadow_st = HYPERVISOR_shared_info->system_time; + + rdtsc_bitshift = HYPERVISOR_shared_info->rdtsc_bitshift; + cpu_freq = HYPERVISOR_shared_info->cpu_freq; + + /* XXX cpu_freq as u32 limits it to 4.29 GHz. Get a better do_div! */ + scale = 1000000000LL << (32 + rdtsc_bitshift); + do_div(scale,(u32)cpu_freq); + st_scale_f = scale & 0xffffffff; + st_scale_i = scale >> 32; + barrier(); } - now = NOW(); + now = __get_s_time(); usec = ((unsigned long)(now-shadow_wc_timestamp))/1000; sec = shadow_tv_sec; usec += shadow_tv_usec; @@ -242,18 +241,6 @@ static inline void do_timer_interrupt(int irq, void *dev_id, struct timeval tv; long long time, delta; -#ifdef XENO_TIME_DEBUG - static u32 foo_count = 0; - foo_count++; - if (foo_count>= 1000) { - s64 n = NOW(); - struct timeval tv; - do_gettimeofday(&tv); - printk("0x%08X%08X %ld:%ld\n", - (u32)(n>>32), (u32)n, tv.tv_sec, tv.tv_usec); - foo_count = 0; - } -#endif /* * The next bit really sucks: * Linux not only uses do_gettimeofday() to keep a notion of @@ -268,7 +255,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id, * updates xtime accordingly. Yuck! */ - /* work out the number of jiffies past and update them */ + /* Work out the number of jiffy intervals passed and update them. */ do_gettimeofday(&tv); time = (((long long)tv.tv_sec) * 1000000) + tv.tv_usec; delta = time - last_irq; @@ -307,24 +294,14 @@ static struct irqaction irq_timer = { void __init time_init(void) { unsigned long long alarm; - u64 cpu_freq = HYPERVISOR_shared_info->cpu_freq; - u64 scale; + u64 __cpu_khz; - cpu_khz = (u32)cpu_freq/1000; + __cpu_khz = HYPERVISOR_shared_info->cpu_freq; + do_div(__cpu_khz, 1000); + cpu_khz = (u32)__cpu_khz; printk("Xen reported: %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); - /* - * calculate systemtime scaling factor - * XXX RN: have to cast cpu_freq to u32 limits it to 4.29 GHz. - * Get a better do_div! - */ - scale = 1000000000LL << 32; - do_div(scale,(u32)cpu_freq); - st_scale_f = scale & 0xffffffff; - st_scale_i = scale >> 32; - printk("System Time scale: %X %X\n",st_scale_i, st_scale_f); - do_gettimeofday(&xtime); last_irq = (((long long)xtime.tv_sec) * 1000000) + xtime.tv_usec; -- 2.30.2